this和call,apply,bind.md

2018.10.15 星期一 14:35

this 几个实例

this关键字被用于指代当前的对象,通常,this指代的是方法中正在被调用的对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
// ## 1
var name='window'
var foo={
name:'foo',
hi:function(){
console.log(this.name)
},
bar:{
name:'bar',
say:function(){
console.log(this.name)
}
}
}
var hello=foo.hi;
hello();// window
foo.hi();// foo
foo.bar.say();// bar

// ## 2
var obj={
name:'obj',
times:[1,2,3,4],
print:function(){
this.times.map(function(v){
console.log(this.name)
})
}
}
obj.print(); // 4个undefined
// ### 改进
var obj={
name:'obj',
times:[1,2,3,4],
print:function(){
this.times.map(function(v){
console.log(this.name)
}.bind(this))
}
}
obj.print(); // 4个obj

// ## 3
var myObj = {
specialFunction: function () {

},
anotherSpecialFunction: function () {
},
getAsyncData: function (cb) {
console.log(this) // myObj
cb();
},
render: function () {
var that = this;
this.getAsyncData(function () {
console.log(this) // window
that.specialFunction();
that.anotherSpecialFunction();
});
this.specialFunction()
}
};
myObj.render();
/* cb: 在这种情况下--$PS:也就是回调函数,没有设置内部函数的this,所以它指向 global/window 对象(即非严格模式下调用的函数未设置this时指向的默认对象)。
同理setTimeout中的回调函数;如果使用箭头函数,this与封闭词法上下文的this保持一致
包括下面的情况
*/
<button onclick="alert((function(){return this})());">
Show inner this
</button>

call,apply 和bind

bind

bind:基于一个现有函数,创建一个新函数,同时提前绑定函数中的this为指定对象。
何时使用: 今后只要希望将一个对象和一个函数中的this,永久绑定,只能用bind。
原理:
1. 创建了一个新函数
2. 在新函数中调用了原函数
3. 永久绑定函数中的this为指定对象。

用法:
基于fun创建一个新函数fun1,并永久绑定fun1中的this为obj
基于fun创建一个新函数fun1,并永久绑定fun1中的this为obj,同时永久绑定fun1中的参数值为参数1,….

1
2
var fun1=fun.bind(obj);     
var fun1=fun.bind(obj,参数1,...)

$PS: 在Javascript中,多次 bind() 是无效的。更深层次的原因, bind() 的实现,相当于使用函数在内部包了一个 call / apply ,第二次 bind() 相当于再包住第一次 bind() ,故第二次以后的 bind 是无法生效的。

1
2
3
4
5
6
7
8
var bar = function(){console.log(this.x);}
var foo = {x:3}
var sed = {x:4}
var func = bar.bind(foo).bind(sed);
func(); //3
var fiv = {x:5}
var func = bar.bind(foo).bind(sed).bind(fiv);
func(); //3

call,apply和bind 比较

#### call apply:IE8+
相同点:临时借(调)用函数,临时替换函数中的this为指定对象。
不同点:传递给借用的函数的参数:
call,要求每个参数独立传入
apply,要求所有参数用数组或集合方式整体传入。
强调: 不创建新函数,而是直接调用原函数。

#### bind: IE9+
相同:都是为了替换函数中的this为指定对象。
不同:
1.创建一个新函数
2.永久绑定this为指定对象,且也可永久绑定部分参数值。
$PS: bind是返回对应函数,便于稍后调用;apply、call则是立即调用 。

$TODO: 模拟实现bind函数

使用

为了巩固加深记忆,下面列举一些常用用法:
1、数组之间追加

1
2
3
4
var array1 = [12 , "foo" , {name:"Joe"} , -2458];
var array2 = ["Doe" , 555 , 100];
Array.prototype.push.apply(array1, array2);
/* array1 值为 [12 , "foo" , {name:"Joe"} , -2458 , "Doe" , 555 , 100] */

2、获取数组中的最大值和最小值
number 本身没有 max 方法,但是 Math 有,我们就可以借助 call 或者 apply 使用其方法。

1
2
3
var numbers = [5, 458 , 120 , -215 ];
var maxInNumbers = Math.max.apply(Math, numbers), //458
maxInNumbers = Math.max.call(Math,5, 458 , 120 , -215); //458

3、验证是否是数组(前提是toString()方法没有被重写过)

1
2
3
functionisArray(obj){
return Object.prototype.toString.call(obj) === '[object Array]' ;
}

4、类(伪)数组使用数组方法
var domNodes = Array.prototype.slice.call(document.getElementsByTagName("*"));

Javascript中存在一种名为伪数组的对象结构。比较特别的是 arguments 对象,还有像调用 getElementsByTagName , document.childNodes 之类的,它们返回NodeList对象都属于伪数组。不能应用 Array下的 push , pop 等方法。

但是我们能通过 Array.prototype.slice.call 转换为真正的数组的带有 length 属性的对象,这样 domNodes 就可以应用 Array 下的所有方法了。

$PS: log函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function log(){
console.log.apply(console, arguments);
};
log(1); //1
log(1,2); //1 2
// ## 1
function log(msg){
console.log(msg);
};
// ## 2
function log(){
var args = Array.prototype.slice.call(arguments);
args.unshift('(app)');
console.log.apply(console, args);
};
// ## 0
// log=console.log.bind(this)
log=console.log.bind(this,'app|')
log(1,2); //app| 1 2

14:46

2018.10.29 星期一 11:25

this 关键字

# this

与其他语言相比,函数的 this 关键字在 JavaScript 中的表现略有不同,此外,在严格模式和非严格模式之间也会有一些差别。

在绝大多数情况下,函数的调用方式决定了this的值。this不能在执行期间被赋值,并且在每次函数被调用时this的值也可能会不同。ES5引入了bind方法来设置函数的this值,而不用考虑函数如何被调用的,ES2015 引入了支持this词法解析的箭头函数(它在闭合的执行上下文内设置this的值)。
## 全局上下文
无论是否在严格模式下,在全局执行上下文中(在任何函数体外部)this 都指代全局对象。

1
2
3
this === window
var a=22
this.a // 22

## 函数上下文
在函数内部,this的值取决于函数被调用的方式。

简单调用

因为下面的代码不在严格模式下,且 this 的值不是由该调用设置的,所以 this 的值默认指向全局对象。
$PS: 可以理解成对象调用,即全局window调用,所以指向全局对象:window

然而,在严格模式下,this将保持他进入执行上下文时的值,所以下面的this将会默认为undefined。
所以,在严格模式下,如果 this 没有被执行上下文(execution context)定义,那它将保持为 undefined。

1
2
3
4
5
6
7
function f(){
console.log(this) // window
}
function f(){
"use strict"
console.log(this) // undefined
}

bind方法

$PS_兼容: #1 [Understanding JavaScript Bind ()]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
if (!Function.prototype.bind) {
Function.prototype.bind = function (oThis) {
if (typeof this !== "function") {
// closest thing possible to the ECMAScript 5 internal IsCallable function
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
}

var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function () {},
fBound = function () {
return fToBind.apply(this instanceof fNOP && oThis
? this
: oThis,
aArgs.concat(Array.prototype.slice.call(arguments)));
};

fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();

return fBound;
};
}

箭头函数节

在箭头函数中,this与封闭词法上下文的this保持一致。在全局代码中,它将被设置为全局对象($PS: 也是当前定义是的上下文)

作为对象的方法节

当函数作为对象里的方法被调用时,它们的 this 是调用该函数的对象。

#### 原型链中的 this
对于在对象原型链上某处定义的方法,同样的概念也适用。如果该方法存在于一个对象的原型链上,那么this指向的是调用这个方法的对象,就像该方法在对象上一样。
#### getter 与 setter 中的 this
再次,相同的概念也适用于当函数在一个 getter 或者 setter 中被调用。用作 getter 或 setter 的函数都会把 this 绑定到设置或获取属性的对象。

作为构造函数节

当一个函数用作构造函数时(使用new关键字),它的this被绑定到正在构造的新对象。

作为一个DOM事件处理函数节

当函数被用作事件处理函数时,它的this指向触发事件的元素(一些浏览器在使用非addEventListener的函数动态添加监听函数时不遵守这个约定)。

作为一个内联事件处理函数节

当代码被内联on-event 处理函数调用时,它的this指向监听器所在的DOM元素:

1
2
3
4
5
6
7
8
9
<button onclick="alert(this.tagName.toLowerCase());">
Show this
</button>
<!-- 上面的 alert 会显示button。注意只有外层代码中的this是这样设置的: -->

<button onclick="alert((function(){return this})());">
Show inner this
</button>
<!-- 在这种情况下,没有设置内部函数的this,所以它指向 global/window 对象(即非严格模式下调用的函数未设置this时指向的默认对象)。 -->

其他参考:
Understanding JavaScript Bind ()

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// ## example 1 
var length = 10;
function fn() {
console.log(this.length);
}

const obj = {
length: 5,
method: function(fn) {
fn();
arguments[0]();
}
};

obj.method(fn, 1); // 10, 2
// ### 变化
const obj = {
length: 5,
method: function(fn) {
fn();
const fun = arguments[0];
fun();
}
};
obj.method(fn, 1); // 10,10


// ## example 2
window.val = 1;
var obj = {
val: 2,
dbl: function() {
this.val *= 2;
val *= 2;
console.log(val);
console.log(this.val);
}
}
obj.dbl(); // 2 4
var func = obj.dbl;
func(); // 8 8

11:40

knowledge is no pay,reward is kindness
0%